"""
1.创建类定义
2.创建对象
3.绑定上位函数 绑定下位函数
"""
import zmq
import inspect
import subprocess
import os,json
import time,platform
import threading    
from urllib.parse import unquote
from urllib.parse import quote

class base_class:
    base_import_lst=[
        "import zmq",
        "import inspect",
        "import subprocess",
        "import os,json,sys",
        "import time,platform",
        "import threading" ,
        "from urllib.parse import unquote",
        "from urllib.parse import quote",
        ]
    @classmethod
    def write_pid(self,pid):
        self.pid_dir=r'E:\my\pid_dir'
        with open(self.pid_dir+os.sep+self.__class__.__name__ ,'w',encoding='utf-8')as f:
            f.write(str(pid))
    _process_dct={}#如果组件另开进程，需要在另开的进程内创建
    _all_obj={}
        #编码
    @classmethod
    def _encode_url(cls,data):
        """
        将含有特殊字符的字符串进行 URL 编码。
        
        :param data: 字符串
        :return: 编码后的字符串
        """
        encoded_data = quote(data, safe='')
        return encoded_data
    #解码
    @classmethod
    def _decode_url(cls,encoded_data):
        """
        将 URL 编码后的字符串解码。
        
        :param encoded_data: 编码后的字符串
        :return: 解码后的字符串
        """
        decoded_data = unquote(encoded_data)
        return decoded_data
    
    @classmethod
    def _encode2(cls,data):
        return cls._encode_url(json.dumps(data))
    @classmethod
    def _decode2(cls,data):
        return json.loads(cls._decode_url(data)) 
    def __init__(self,**kwargs):
        self.kwargs=kwargs
        self.project_dir_path=kwargs.get("project_dir_path")#项目所在目录
        self.recv_port=kwargs.get("recv_port")#当前进程通信的port
        self.process_id=kwargs.get("process_id")#当前进程的id
        self.flow_id=kwargs.get("flow_id","None")#流程id
        self.temp_id=kwargs.get("temp_id","None")#流程中被拖拽的组件id
        self.temp_name=kwargs.get("temp_name")#流程中的组件名称
        self.db_temp_id=kwargs.get("db_temp_id")#模板组件id
        self.db_temp_level=kwargs.get("db_temp_level")#组件版本号
        if self.process_id is not None:
            self._process_dct[self.process_id].create_
        self.init(**kwargs)
        self.in_params=kwargs.get("in_params")
        self.out_params=kwargs.get("out_params")
        self._name=kwargs.get("name")
        self._all_obj[self._name]=self
    @classmethod
    def create_class(cls,python_str):
        exec(python_str)
    @classmethod
    def delete_class(cls,class_name):
        exec("del "+class_name)

    #创建当前进程与其他进程通信 的收发通道
    def createsend_message_and_recv(self):
        th2 = threading.Thread(target=self._recv, args=())
        th2.setDaemon(True)
        th2.start()
        th2.join(0)
    #将next后需要执行的数据 传入进程中
    def send_next_data(self,**kwargs):
        pass
    #非相同进程时 send_next替代run_next
    def send_next(self,**kwargs):
        pass
    #检查传参是否全部传递成功
    def run_next(self,**kwargs):
        for key in kwargs:
            if key in self.in_params and self.in_params[key]=="_&":
                self.in_params[key]=kwargs[key]
        #检查参数是否全部传递成功  也即in_params里面的value全为True
        if all(value != "_&" for value in self.in_params.values()):
            #传递成功，执行run函数
            self.run_mid(self.in_params)
    def run_mid(self,kwargs):
        print(self._name,"开始运行")
        t=time.time()
        out_params=self.run(**kwargs)
        self.in_params={k:'_&'for k in self.in_params }
        print(self._name,"结束运行",'%.2f'%(time.time()-t))
        for key,value in self.out_params.items():
            self._all_obj[value['out_obj']].run_next(**{v:out_params[k]for k,v in value['out_params'].items()})
    def init(self,**kwargs):
        pass
    def run(self,**kwargs):
        return kwargs
    def _getsource(self):
        return inspect.getsource(self.__class__)
    def _delete_process(self):
        # 终止进程
        os.kill(self.process_id, 9)  # 9 表示SIGKILL信号，强制结束进程
    def _recv(self):
        self.recv_port=5555
        context = zmq.Context()
        self.socket = context.socket(zmq.REP)
        self.socket.bind(f"tcp://*:{self.recv_port}")  # 绑定到端口 5555
        while True:
            # 接收消息
            message = self.socket.recv_string()
            # print(f"Received request: {message}")
            # 处理消息
            response = f"Response to {message}"
            # 发送响应
            self.socket.send_string(response)
            print(message)
            
    # 检查连接状态
    def is_connected(self,socket):
        try:
            socket.send_string("Check", zmq.NOBLOCK)
            socket.recv_string(zmq.NOBLOCK)
            return True
        except zmq.ZMQError:
            return False
    def send_message(self,server_ip="127.0.0.1",port=5555,message="test"):
        context = zmq.Context()
        print(server_ip,port)
        # 创建一个REQ类型的套接字
        socket = context.socket(zmq.REQ)
        # 设置接收消息的超时时间为500毫秒（0.5秒）
        socket.setsockopt(zmq.RCVTIMEO, 500)

        # 连接到远程服务
        socket.connect(f"tcp://{server_ip}:{port}")
        # socket.connect("tcp://localhost:5555")

        socket.send_string(message)
        # 尝试接收响应，如果在0.5秒内没有收到则会抛出zmq.error.Again异常
        try:
            message = socket.recv_string()
            print(f"Received reply: {message}")
            res=message
        except zmq.error.Again as e:
            print("超时",e)
            res=False
            raise Exception("er")
        return res
    def create_bat_or_shell(self):
        run_process_dir=os.path.join(self.project_dir_path,"run_process",self.flow_id+'_'+self.temp_id,self.temp_name)+os.sep
        try:os.makedirs(run_process_dir)
        except:pass
        
        self.py_file_path=run_process_dir+"server.py"
        self.log_file_path1=run_process_dir+"log1.log"
        self.log_file_path2=run_process_dir+"log2.log"
        if self.check_windows()is True:
            self.path1=run_process_dir+"server_1.bat"
            self.path2=run_process_dir+"server_2.bat"
            with open(self.path1,'w',encoding='utf-8')as f:
                f.write('\n'.join(['@echo off',f"start /B {self.path2}"]))
            with open(self.path2,'w',encoding='utf-8')as f:
                f.write('\n'.join(['@echo off',
                                   'setlocal',
                                   ':: 设置日志文件的路径',
                                   f'set LOG_FILE=f{self.log_file_path2}',
                                   ':: 执行Python脚本,并将输出和错误重定向到同一个日志文件  ',
                                   f'python -u {self.py_file_path} > "%LOG_FILE%" 2>&1 ',
                                   ":: 结束批处理文件",
                                   "endlocal",
                                   ]))
        else:pass
    def create_py_file(self):
        run_process_dir=os.path.join(self.project_dir_path,"run_process",self.flow_id+'_'+self.temp_id,self.temp_name)+os.sep
        try:os.makedirs(run_process_dir)
        except:pass
        self.py_file_path=run_process_dir+"server.py"
        self.import_lst=["import paho.mqtt.client as mqtt"]
        #创建py文件
        with open(self.py_file_path,'w',encoding="utf-8") as f:
            write_str="\n".join(self.base_import_lst)+"\n"+"\n".join(self.import_lst)+"\n"+base_class_obj._getsource()+"\n"+self._getsource()+"\n".join([
            'if __name__=="__main__":',
            f'    {self.__class__.__name__ }.write_pid(os.getpid())',
            f'    obj={self.__class__.__name__ }(**{self.kwargs})',
            '    obj.run_server()',
            ])
            f.write(write_str)

    def _create_process(self):
        # 构建命令行参数
        command = ['python',self.py_file_path]
        subprocess.Popen(command)
        
        # 打开文件以追加模式写入
        with open(self.log_file_path1, 'a') as output_file:
            # 启动Python脚本，并将输出重定向到文件
            process = subprocess.Popen(command, stdout=output_file, stderr=output_file,bufsize=0
                    ,universal_newlines=True,  # 使用文本模式                   
                                       )

    def create_obj(self):
        try:
            #检查目标服务是否启动
            self.send_message(message="A")
        except:
            self.create_bat_or_shell()
            self.create_py_file()
            self._create_process()
        print("创建进程结束")
        return self.recv_port

    def check_windows(self):
        # 判断操作系统
        if os.name == 'nt':
            print("当前环境是 Windows")
            return True
        elif os.name == 'posix':
            if os.uname().sysname == 'Linux':
                return False
base_class_obj=base_class()
class zz(base_class):
    def init(self,**kwargs):pass
    def run(self,**kwargs):
        pass
if __name__=="__main__":
    a=base_class(**{
        "name":"obj1",
        "in_params":{"p1":"_&","p2":"_&"},
    "out_params":{
        1:{"out_obj":"obj2",
                "out_params":{
                    "p1":"params1"
                }},
    
    }
    })
    b=base_class(**{
        "name":"obj2",
        "in_params":{"params1":"_&","params2":"_&"},
    "out_params":{
    
    }
    })
    
    c=zz(**{
        "name":"obj3",
        "in_params":{"p1":"_&","p2":"_&"},
    "out_params":{
        1:{"out_obj":"obj2",
                "out_params":{
                    "p2":"params2"
                }},
    
    }
    })
    a.run_next(**{"p1":1,"p2":1})
    c.run_next(**{"p1":1,"p2":1})
    {
    "base_info": {
        "idx": 1
    },
    "dict": {
        "1": {
            "flow_chat_base_info": {
                "idx": 6,
                "flow_chat_name": "发"
            },
            "flow_chat_use_compontent": {
                "1": {
                    "temp_name": "mqtt初始化",
                    "name": "mqtt初始化",
                    "x": 69.39999389648438,
                    "y": 65.19999694824219,
                    "out_idx": 1,
                    "in_idx": 0,
                    "out": {
                        "1": {
                            "out_id": "2",
                            "name": "mqtt初始化->收mqtt数据"
                        }
                    },
                    "in": {}
                },
                "2": {
                    "temp_name": "收mqtt数据",
                    "name": "收mqtt数据",
                    "x": 70.39999389648438,
                    "y": 177.1999969482422,
                    "out_idx": 1,
                    "in_idx": 0,
                    "out": {
                        "1": {
                            "out_id": "3",
                            "name": "收mqtt数据->手写函数"
                        }
                    },
                    "in": {}
                },
                "3": {
                    "temp_name": "手写函数",
                    "name": "手写函数",
                    "x": 76.39999389648438,
                    "y": 255.1999969482422,
                    "out_idx": 1,
                    "in_idx": 0,
                    "out": {
                        "1": {
                            "out_id": "4",
                            "name": "手写函数->写数据库"
                        }
                    },
                    "in": {}
                },
                "4": {
                    "temp_name": "写数据库",
                    "name": "写数据库",
                    "x": 69.39999389648438,
                    "y": 336.1999969482422,
                    "out_idx": 1,
                    "in_idx": 0,
                    "out": {
                        "1": {
                            "out_id": "6",
                            "name": "写数据库->手写函数"
                        }
                    },
                    "in": {}
                },
                "5": {
                    "temp_name": "发websocket数据",
                    "name": "发websocket数据",
                    "x": 41.399993896484375,
                    "y": 491.1999969482422,
                    "out_idx": 0,
                    "in_idx": 0,
                    "out": {},
                    "in": {}
                },
                "6": {
                    "temp_name": "手写函数",
                    "name": "手写函数",
                    "x": 69.39999389648438,
                    "y": 422.1999969482422,
                    "out_idx": 1,
                    "in_idx": 0,
                    "out": {
                        "1": {
                            "out_id": "5",
                            "name": "手写函数->发websocket数据"
                        }
                    },
                    "in": {}
                }
            }
        }
    }
}
    
{
    "now_server":"当前服务器AAA",
    "now_process":"当前进程AAA",
    "next_node":"下一级流程",
    "x":1,
    "y":2,
    "name":"ss",
    "temp_name":"模板名称",
    "in":{},
    "out":{},
    "out_idx":0,
    "in_idx":0,
}